from dev import *
import time
from logger import log
import socket
import select
from protocol import *
import re
import hashlib, base64
import struct
import json
import traceback
import inspect
try:
	# Python 2.x
	from Queue import Queue
except ImportError:
	# Python 3.x
	from queue import Queue
try:
	# Python 2.x
	xrange(0)
except NameError:
	# Python 3.x
	xrange = range
from object import Object

class UnitProto(Object):

	STATE_NEW = 0
	STATE_HANDSHAKE = 1
	STATE_ESTABLISHED = 2

	def __init__(self, gameEngine, dispatcher, client, info):
		self.gameEngine = gameEngine
		self.client = client
		self.ip, self.port = info
		self.timeConnected = time.time()
		self.channelState = UnitProto.STATE_NEW
		self.lastContact = time.time()
		self.locked = False
		self.dead = False
		self.dispatcher = dispatcher

		self.iqueue = Queue()
		self.oqueue = Queue()
		self.equeue = Queue()

		log('Unit constructed for %s'%self.ip)

	def lock(self):
#		print 'lock: '+str(traceback.extract_stack())
		while self.locked:
			log('DEADLOCK IN UnitProto.lock()')
		self.locked = True

	def unlock(self):
#		print 'unlock: '+str(traceback.extract_stack())
		while not self.locked:
			log('DOUBLE-UNLOCK IN UnitProto.unlock()')
		self.locked = False

	def hasData(self):
		result = False
		self.lock()
		iready, oready, eready = select.select([self.client],[],[], 0)
		if self.client in iready:
			result = True
		self.unlock()
		return result

	def read(self):
		while(self.hasData()):
			self.lock()
			if self.channelState == UnitProto.STATE_NEW:

				try:
					data = self.client.recv(1024)
#					log('RECV  `%s`'%repr(data))
#					log('ASCII `%s`'%data)
					if not len(data):
						raise Exception
				except:
					self.unlock()
					return self.disconnect()

				lines = data.split(b'\n')
				hskey = None
				host = 'localhost'
				for line in lines:
					line = line.strip()
					parts = re.match(b'(.*): (.*)', line)
					if parts:
						key, value = parts.group(1).lower(), parts.group(2)
						if key == b'sec-websocket-key':
							hskey = value
						if key == b'host':
							host = value

				if hskey:
					sha1 = hashlib.sha1()
					sha1.update(hskey.strip())
					sha1.update(A_KIND_OF_WEBSOCKET_MAGIC)
					respkey = base64.b64encode(sha1.digest()).strip()
					self.write('HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n'%(respkey))
					self.channelState = UnitProto.STATE_ESTABLISHED
					log('Switching protocols for %s'%self.ip)

					# Virtual call
					self.onConnect()

				else:
					log('Bad HTTP upgrade request for websockets')
					self.unlock()
					return self.disconnect()

			else:
	#			packets = functions.iterload(data)

				typeData = self.client.recv(1)
				if not len(typeData) or ord(typeData[0]) != 0x81:
					log('Malformed WebSocket frame from %s'%self.ip)
					self.unlock()
					return self.disconnect()

				shift = 0

				lenData = self.client.recv(1)

				packLen = ord(lenData[0]) & 0x7F
				if packLen == 126:
					lenData = self.client.recv(2)
					packLen = struct.unpack('>H', lenData)[0]
					shift = 2
				elif packLen == 127:
					lenData = self.client.recv(8)
					packLen = struct.unpack('>Q', lenData)[0]
					shift = 8

				maskStr = self.client.recv(4)
				dataStr = list(self.client.recv(packLen))

				mask = list(ord(x) for x in maskStr);

				data = ''

				for i in xrange(len(dataStr)):
					data += chr(ord(dataStr[i])^mask[i%4])

				try:
					packet = Packet(data)
					if not packet.isValid():
						raise Exception
					packet.set('sender', self.ip)
					self.iqueue.put(packet)
				except:
					log('Received malformed JSON from %s'%self.ip)
					self.unlock()
					return self.disconnect()
			self.unlock()

#		log('Read complete')

		self.lastContact = time.time()

	def write(self, data):
#		log('Adding to queue: `%s`'%data)
		self.oqueue.put(data)

	def _write(self, data):
		self.lock()
		try:
			self.client.send(data)
		except:
			log('Error sending data')
			self.unlock()
			return self.disconnect()
		self.unlock()

	def addEvent(self, event):
		self.equeue.put(event)

	def disconnect(self):
		if not self.dead:
			self.dead = True

			# Virtual call
			self.onDisconnect()

			try:
				self.client.close()
			except:
				log('[EXCEPTION] Failed to close socket')
			return False

	def processQueue(self):
		while True:
			try:
				event = self.equeue.get(False)
			except:
				break

			log('%s %%%% %s'%(self.ip,event))

			##############################
			# Do something with event...

			# Virtual call
			if not self.processEvent(event):
				return

			# End
			##############################

		while True:
			try:
				packet = self.iqueue.get(False)
			except:
				break

			log('%s >> %s'%(self.ip,packet))

			##############################
			# Do something with packet...

			# Virtual call
			if not self.processPacket(packet):
				return

			# End
			##############################

		while True:
			try:
				data = self.oqueue.get(False)
			except:
				break

			log('%s << %s'%(self.ip,data))
			if data.__class__ == Packet:
				s = data.toJSON()
				sLen = len(s)#+4
				raw = [chr(0x81)]
				if(sLen<126):
					raw.append(chr(sLen))
				elif(sLen<65536):
					raw.append(chr(126))
					raw.extend(struct.pack('>H', sLen))
				else:
					raw.append(chr(127))
					raw.extend(struct.pack('>Q', sLen))
#				raw.extend([chr(0x0), chr(0x0), chr(0x0), chr(0x0)])
				raw = ''.join(raw)+s;
##				print repr(raw);
				self._write(raw)
#				print 'SENT %s'%data.toJSON()
			elif data.__class__ == str:
				self._write(data)
			else:
				raise Exception('Attempt to send some shit that is neither Packet nor str! Fucking developer!')

	def onConnect(self):
		raise VirtualException('`onConnect` is virtual and needs to be overrided!')
		pass

	def processEvent(self, event):
		raise VirtualException('`processEvent` is virtual and needs to be overrided!')
		pass
